#pragma once
#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_logger.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <iostream>
#include <memory>
#include <unordered_map>
#include <mutex>
#include <cstring>

namespace mymq
{
    // 绑定信息类
    struct Binding
    {
        using Ptr = std::shared_ptr<Binding>;
        std::string exchange_name;
        std::string queue_name;
        std::string binding_key; // 与消息进行匹配
        Binding() {}
        Binding(const std::string &ename, const std::string &qname, const std::string &key) : exchange_name(ename),
                                                                                              queue_name(qname),
                                                                                              binding_key(key)
        {
        }
        ~Binding() {}
    };
    // 持久化绑定信息管理类
    // 队列与绑定信息的对应关系，一个队列对应唯一一个binding_key
    using QueueBindingMap = std::unordered_map<std::string, Binding::Ptr>;
    // 一个交换机对应其绑定的所有队列信息，通过交换机名字就能找到所有与他相关的队列信息
    using BindingMap = std::unordered_map<std::string, QueueBindingMap>;
    class BindingMapper
    {
    public:
        BindingMapper(const std::string &dbfile) : _sql_helper(dbfile)
        {
            std::string path = FileHelper::parentderectory(dbfile);
            FileHelper::createderectory(path);
            assert(_sql_helper.Open());
            CreateTable();
        }
        void CreateTable()
        {
            std::stringstream sql;
            sql << "create table if not exists binding_table(";
            sql << "exchange_name varchar(32),";
            sql << "queue_name varchar(32),";
            sql << "binding_key varchar(128));";
            assert(_sql_helper.Exec(sql.str(), nullptr, nullptr));
        }
        void RemoveTable()
        {
            std::stringstream sql;
            sql << "drop table if exists binding_table;";
            _sql_helper.Exec(sql.str(), nullptr, nullptr);
        }
        bool Insert(Binding::Ptr &binding)
        {
            std::stringstream sql;
            sql << "insert into binding_table values(";
            sql << "'" << binding->exchange_name << "',";
            sql << "'" << binding->queue_name << "',";
            sql << "'" << binding->binding_key << "');";
            return _sql_helper.Exec(sql.str(), nullptr, nullptr);
        }
        // 这两个名字构成绑定关系数据的唯一键
        void Remove(const std::string &ename, const std::string &qname)
        {
            std::stringstream sql;
            sql << "delete from binding_table where ";
            sql << "exchange_name=" << "'" << ename << "' and ";
            sql << "queue_name=" << "'" << qname << "';";
            _sql_helper.Exec(sql.str(), nullptr, nullptr);
        }
        // 移除指定交换机的所有绑定信息
        void RemoveExchangeBinding(const std::string &ename)
        {
            std::stringstream sql;
            sql << "delete from binding_table where ";
            sql << "exchange_name=" << "'" << ename << "';";
            _sql_helper.Exec(sql.str(), nullptr, nullptr);
        }
        // 移除指定消息队列的所有绑定信息
        void RemoveMsgQueueBinding(const std::string &qname)
        {
            std::stringstream sql;
            sql << "delete from binding_table where ";
            sql << "queue_name=" << "'" << qname << "';";
            _sql_helper.Exec(sql.str(), nullptr, nullptr);
        }
        // 恢复数据
        BindingMap Recovery()
        {
            std::string sql = "select * from binding_table;";
            BindingMap result;
            _sql_helper.Exec(sql.c_str(), SelectCallBack, &result);
            return result;
        }
        ~BindingMapper() {}

    private:
        static int SelectCallBack(void *arg, int numcol, char **row, char **fields)
        {
            BindingMap *result = static_cast<BindingMap *>(arg);
            Binding::Ptr bind = std::make_shared<Binding>(row[0], row[1], row[2]);
            // 防止当前绑定信息已经存在而新绑定信息将其覆盖，从而达到不存在即创建，存在则不新增
            QueueBindingMap &qbm = (*result)[bind->exchange_name];
            qbm.insert(std::make_pair(bind->queue_name, bind));
            return 0;
        }

    private:
        SqliteHelper _sql_helper;
    };
    class BindingManager
    {
    public:
        using Ptr = std::shared_ptr<BindingManager>;
        BindingManager(const std::string &dbfile) : _mapper(dbfile)
        {
            // 初始化数据
            _bindings = _mapper.Recovery();
        }
        // 绑定
        bool Bind(const std::string &ename, const std::string &qname, const std::string &key, bool durable)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            // 如果在当前交换机的map对照关系并且在队列中的map关系映射中存在的话说明已经有了绑定关系
            if (it != _bindings.end() && it->second.find(qname) != it->second.end())
            {
                return true;
            }
            // bind
            Binding::Ptr bptr = std::make_shared<Binding>(ename, qname, key);
            if (durable == true)
            {
                bool ret = _mapper.Insert(bptr);
                if (ret == false)
                    return false;
            }
            auto &mqbg = _bindings[ename];
            mqbg.insert(std::make_pair(qname, bptr));
            return true;
        }
        // 解除绑定
        bool unBind(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(ename);
            if (eit == _bindings.end())
                return false;
            auto qit = eit->second.find(qname);
            if (qit == eit->second.end())
                return false;
            // 存在该绑定关系
            // 删除
            _mapper.Remove(ename, qname);
            _bindings[ename].erase(qname);
            return true;
        }
        // 解除与该交换机相关的绑定信息
        void removeExchangeBindings(const std::string &ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(ename);
            if (eit == _bindings.end())
                return; // 没找到
            // 找到了
            _bindings.erase(ename);
            _mapper.RemoveExchangeBinding(ename);
        }
        // 解除与该队列相关的绑定信息
        void removeMsgQueueBindings(const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.RemoveMsgQueueBinding(qname);
            for (auto it = _bindings.begin(); it != _bindings.end(); ++it)
            {
                it->second.erase(qname);
            }
        }
        // 查找指定交换机的所有绑定信息
        QueueBindingMap getExchangeBindings(const std::string &ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(ename);
            if (eit == _bindings.end())
            {
                return QueueBindingMap();
            }
            return eit->second;
        }
        // 获取一个指定的绑定信息
        Binding::Ptr getBinding(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(ename);
            if (eit == _bindings.end())
                return Binding::Ptr();
            auto qit = eit->second.find(qname);
            if (qit == eit->second.end())
                return Binding::Ptr();
            return qit->second;
        }
        bool Exists(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eit = _bindings.find(ename);
            if (eit == _bindings.end())
                return false;
            auto qit = eit->second.find(qname);
            if (qit == eit->second.end())
                return false;
            return true;
        }
        size_t size()
        {
            size_t n = 0;
            std::unique_lock<std::mutex> lock(_mutex);
            for (auto start = _bindings.begin(); start != _bindings.end(); ++start)
            {
                n += start->second.size();
            }
            return n;
        }
        void Clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.RemoveTable();
            _bindings.clear();
        }
        ~BindingManager() {}

    private:
        std::mutex _mutex;
        BindingMapper _mapper;
        BindingMap _bindings;
    };
}